#include <logger.h>
#include <stdio.h>

#include "datacenter.h"
#include "datacenterprivate.h"
#include "iot_debug.h"
#include "tools/auto_guard.h"
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/tools.h"
#include "util_tools.h"
#include "config.h"

using namespace rapidjson;
using namespace DC;

std::string DataCenter::GetTimeStamp(tm *tm )
{
    if (tm == nullptr) {
        time_t rawtime;
        time( &rawtime );
        tm = localtime( &rawtime );
    }

    std::ostringstream os;
    os << std::setfill('0')
       << std::setw(4) << tm->tm_year+1900
       << "-"
       << std::setw(2) << tm->tm_mon+1
       << "-"
       << std::setw(2) << tm->tm_mday
       << "T"
       << std::setw(2) << tm->tm_hour
       << ":"
       << std::setw(2) << tm->tm_min
       << ":"
       << std::setw(2) << tm->tm_sec
       << ".000+0800";

    return os.str();
}

time_t DataCenter::GetTimeStamp(std::string strTm)
{
    struct tm t;
    if (strTm == "")
    {
        return time( NULL );
    }
    sscanf(strTm.c_str(), "%d-%d-%dT%d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
    t.tm_year -= 1900;
    t.tm_mon -= 1;
    return mktime(&t);
}

bool DataCenter::containedItem(const ModelItems &items, const ModelItem &item)
{
    for (auto i=items.begin(); i!=items.end(); i++) {
        if (item.compare(*i))
            return true;
    }
    return false;
}

bool DataCenter::hasModelName(const std::string &name) const
{
    return std::find(d_->model_names_.begin(), d_->model_names_.end(), name) != d_->model_names_.end();
}

bool DataCenter::hasModel(const std::string &name) const
{
    return d_->models_.find(name) != d_->models_.end();
}

void DataCenter::getUartMode(ComSettings &uartinfo)
{
    if(d_->uart_infos_.uart_name.find(uartinfo.uart_name) != std::string::npos)
    {
        uartinfo.mode = d_->uart_infos_.mode;
        uartinfo.app_name = d_->uart_infos_.appname;
    }
}

const DevInfo *DataCenter::findDevInfo(const std::string &model_name,
                                       const std::string &port,
                                       const std::string &addr,
                                       const std::string &desc) const
{
    auto it = d_->model_devs_.find(model_name);
    if (it == d_->model_devs_.end()) {
        log_info("not found %s\n", model_name.c_str());
        return nullptr;
    }

    auto it_dev = it->second.find(port);
    if (it_dev == it->second.end()) {
        log_info("not found %s in %s\n", port.c_str(), model_name.c_str());
        return nullptr;
    }

    for (auto it_info=it_dev->second.begin(); it_info!=it_dev->second.end(); ++it_info)
    {
        if (it_info->addr==addr && it_info->desc==desc)
            return &*it_info;
    }

    log_info("not match: %s  %s\n", addr.c_str(), desc.c_str());
    /*
    for (auto it_info=it_dev->second.begin(); it_info!=it_dev->second.end(); ++it_info)
    {
        log_info("%s %s\n", it_info->addr.c_str(), it_info->desc.c_str());
    }
    */

    return nullptr;
}

const ModelItems *DataCenter::findModel(const std::string &model_name) const
{
    Models::const_iterator it=d_->models_.find(model_name);
    if (it != d_->models_.end())
        return &it->second;
    return nullptr;
}

const std::string &DataCenter::clientId() const
{
    return d_->clientId();
}

int DataCenter::subTopic(const char *topic, int qos)
{
    return d_->subTopic(topic, qos);
}

int DataCenter::unsubTopic(const char *topic)
{
    return d_->unsubTopic(topic);
}

int DataCenter::sendMsg(const char *topic, const char *msg, int qos)
{
    log_info("%s -> %s\n", topic, msg);
    return d_->pubTopic(topic, msg, qos);
}

DataCenter::DataCenter() :
    d_(new DataCenterPrivate(this))
{

}

DataCenter::~DataCenter()
{
    deinit();
    delete d_;
}

int DataCenter::init(const char *ip, uint16_t port, const char *user, const char *passwd, const char *clientId)
{
    return d_->init(ip, port, user, passwd, clientId);
}

int DataCenter::init(const char *client_id)
{
    const char * ip = "127.0.0.1";
    uint16_t port = 1883;
    const char *user = "admin";
    const char *passwd = "password";

    Document doc;
    if (parseFile(RUNTIME_PATH BROKER_FILE, doc)) {
        ip = getString(doc, "ServiceIp", ip);
        port = getInt(doc, "ServicePort", port);
        user = getString(doc, "UserName", "admin");
        passwd = getString(doc, "Password", "password");
    }

    return d_->init(ip, port, user, passwd, client_id);
}

void DataCenter::deinit()
{
    d_->deinit();
}

bool DataCenter::logined() const
{
    return d_->logined();
}

uint16_t DataCenter::newToken()
{
    return d_->newToken();
}

void DataCenter::getUartInfo(const char *name)
{
    d_->registerGetUartInfo();
    std::stringstream ss;

    ss << "{\"token\": \"" << ++d_->token_ << "\", "
       << "\"timestamp\": \"" << GetTimeStamp() << "\", "
       << "\"port\": \"" << name << "\"}";

    sendMsg(d_->topics_->get_uartinfo, ss.str());
}

bool DataCenter::getUartInfo(const char *name, size_t wait_s)
{
    getUartInfo(name);
    return d_->query_uartinfo_sem_.timedwait(wait_s*1000) == 0;
}

void DataCenter::getModelSchema()
{
    d_->registerGetModelSchema();

    std::stringstream ss;

    ss << "{\"token\": \"" << ++d_->token_ << "\", "
       << "\"timestamp\": \"" << GetTimeStamp() << "\", "
       << "\"body\": []}";

    sendMsg(d_->topics_->get_modelschema, ss.str());
}

bool DataCenter::getModelSchema(size_t wait_s)
{
    mtx_.lock();
    d_->model_names_.clear();
    mtx_.unlock();

    getModelSchema();

    return (0 == d_->query_model_schema_sem_.timedwait(wait_s*1000)) ? true : false;
}

void DataCenter::deleteModel(const char *name)
{
    d_->registerDeleteModel();

    std::stringstream ss;

    ss << "{\"token\": \"" << ++d_->token_ << "\", "
       << "\"timestamp\": \"" << GetTimeStamp() << "\", "
       << "\"body\": [" << "\"" << name << "\"]}";

    sendMsg(d_->topics_->delete_model, ss.str());
}

void DataCenter::getModel(const char *name)
{
    d_->registerGetModel();

    std::stringstream ss;

    ss << "{\"token\": \"" << ++d_->token_ << "\", "
       << "\"timestamp\": \"" << GetTimeStamp() << "\", "
       << "\"body\": [" << "\"" << name << "\"]}";

    sendMsg(d_->topics_->get_model, ss.str());
}

bool DataCenter::getModel(const std::string &name, size_t wait_s)
{
    mtx_.lock();
    d_->models_.erase(name);
    mtx_.unlock();

    getModel(name.c_str());

    time_t t0 = getCurrentTime();
    do {
        usleep(10000);
        AUTO_GUARD(gd, MMutex, mtx_);
        if (hasModel(name))
            return true;
    } while (!checkTime(t0, getCurrentTime(), wait_s));

    return false;
}


void DataCenter::getModels(const std::list<std::string> &names)
{
    d_->registerGetModel();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();
    for (auto name : names) {
        writer.String(name.c_str());
    }
    writer.EndArray();
    writer.EndObject();

    sendMsg(d_->topics_->get_model, buffer.GetString());
}

bool DataCenter::getModels(const std::list<std::string> &names, size_t wait_s)
{
    std::list<std::string> tmp;
    mtx_.lock();
    for (auto n=names.begin(); n!=names.end(); n++) {
        tmp.push_back(*n);
        d_->models_.erase(*n);
    }
    mtx_.unlock();

    getModels(names);

    time_t t0 = getCurrentTime();
    do {
        usleep(10000);
        mtx_.lock();
        for (auto m=tmp.begin(); m!=tmp.end(); ) {
            if (hasModel(*m))
                m = tmp.erase(m);
            else
                m++;
        }
        mtx_.unlock();
        if (tmp.empty())
            return true;
    } while (!checkTime(t0, getCurrentTime(), wait_s));

    return false;
}

bool DataCenter::CheckReturnStatus(uint32_t token)
{
    int cnt = 0;

    do
    {
        if(token == atoi(d_->returntoken_.c_str()))
        {
            if(d_->returnstatus_.compare("OK") == 0)
                return true;
            else
                return false;
        }

        mSleep(100);
        cnt++;

    } while (cnt < 100);

    return false;
}

bool DataCenter::setModel(const char *model_name, const ModelItems &items)
{
    d_->registerSetModel();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());
    writer.Key("model");
    writer.String(model_name);

    writer.Key("body");
    writer.StartArray();

    for (auto i=items.begin(); i!=items.end(); i++)
    {
        writer.StartObject();
        writer.Key("name");
        writer.String(i->name.c_str());
        writer.Key("type");
        writer.String(i->type.c_str());
        writer.Key("unit");
        writer.String(i->unit.c_str());
        writer.Key("deadzone");
        writer.String(i->deadzone.c_str());
        writer.Key("ratio");
        writer.String(i->ratio.c_str());
        writer.Key("isReport");
        writer.String(i->isReport.c_str());
        writer.Key("userdefine");
        writer.String(i->userdefine.c_str());
        writer.EndObject();

        if (buffer.GetSize() > MQTT_MAX_SIZE)
        {
            writer.EndArray();
            writer.EndObject();
            sendMsg(d_->topics_->set_model, buffer.GetString());

            if(!CheckReturnStatus(d_->token_))
            {
                return false;
            }

            buffer.Clear();
            writer.Reset(buffer);
            usleep(5000);

            writer.StartObject();
            writer.Key("token");
            writer.String(u32toa(++d_->token_).c_str());
            writer.Key("timestamp");
            writer.String(GetTimeStamp().c_str());
            writer.Key("model");
            writer.String(model_name);

            writer.Key("body");
            writer.StartArray();
        }
    }
    writer.EndArray();
    writer.EndObject();

    sendMsg(d_->topics_->set_model, buffer.GetString());

    return CheckReturnStatus(d_->token_);
}

void DataCenter::setUart(ComSettings *uartinfo_)
{
    d_->registerSetUart();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());
    writer.Key("appName");
    writer.String("SyyGather");
    writer.Key("mode");
    writer.String("only");
    writer.Key("port");
    writer.String(uartinfo_->uart_name.c_str());

    writer.Key("param");
    writer.StartObject();
    writer.Key("baudRate");
    writer.String(std::to_string(uartinfo_->uart.baudrate).c_str());
    writer.Key("byteSize");
    writer.String("8");
    writer.Key("parity");
    writer.String(uartinfo_->uart.paritystr_.c_str());
    writer.Key("stopBits");
    writer.String("1");

    writer.EndObject();
    writer.EndObject();
    sendMsg(d_->topics_->set_uart, buffer.GetString());
}

void DataCenter::getDevGUID(const char *model_name, const char *port,
                            const char *addr, const char *desc)
{
    d_->registerGetDevGUID();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();
    writer.StartObject();
    writer.Key("model");
    writer.String(model_name);
    writer.Key("port");
    writer.String(port);
    writer.Key("addr");
    writer.String(addr);
    if (desc) {
        writer.Key("desc");
        writer.String(desc);
    }
    writer.EndObject();
    writer.EndArray();

    writer.EndObject();

    sendMsg(d_->topics_->get_dev_guid, buffer.GetString());
}

void DataCenter::getDevGUID(const DevInfo &dev_info)
{
    return getDevGUID(dev_info.model.c_str(), dev_info.port.c_str(),
                      dev_info.addr.c_str(), dev_info.desc.c_str());
}

void DataCenter::registerDev(const DevInfo &dev_info)
{
    std::list<DevInfo> devs;
    devs.push_back(dev_info);
    registerDevs(devs);
}

bool DataCenter::registerDevs(const std::list<DevInfo> &devs)
{
    d_->registerRegisterDev();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();

    for (auto it=devs.begin(); it!=devs.end(); it++)
    {
        const DevInfo &dev_info = *it;

        writer.StartObject();
        writer.Key("model");
        writer.String(dev_info.model.c_str());
        writer.Key("port");
        writer.String(dev_info.port.c_str());
        writer.Key("addr");
        writer.String(dev_info.addr.c_str());
        writer.Key("desc");
        writer.String(dev_info.desc.c_str());
        writer.Key("manuID");
        writer.String(dev_info.manuID.c_str());
        writer.Key("manuName");
        writer.String(dev_info.manuName.c_str());
        writer.Key("ProType");
        writer.String(dev_info.protocolName.c_str());
        writer.Key("deviceType");
        writer.String(dev_info.deviceType.c_str());
        writer.Key("isReport");
        writer.String(dev_info.isReport.c_str());
        writer.Key("nodeID");
        writer.String(dev_info.nodeID.c_str());
        writer.Key("productID");
        writer.String(dev_info.productID.c_str());
        writer.EndObject();

        if (buffer.GetSize() > MQTT_MAX_SIZE)
        {
            writer.EndArray();
            writer.EndObject();
            sendMsg(d_->topics_->register_dev, buffer.GetString());

            if(!CheckReturnStatus(d_->token_))
            {
                return false;
            }

            buffer.Clear();
            writer.Reset(buffer);
            usleep(500000);

            writer.StartObject();
            writer.Key("token");
            writer.String(u32toa(++d_->token_).c_str());
            writer.Key("timestamp");
            writer.String(GetTimeStamp().c_str());

            writer.Key("body");
            writer.StartArray();
        }
    }
    writer.EndArray();
    writer.EndObject();

    sendMsg(d_->topics_->register_dev, buffer.GetString());

    return CheckReturnStatus(d_->token_);
}

void DataCenter::SubYKCmdTopic()
{
    subTopic("+/action/request/*/remoteCtrl");
}

void DataCenter::listenDevRegisterNotify()
{
    d_->registerListenDevRegisterNotify();
    subTopic("datacenter/notify/spont/allApp/Updateregister");
}

void DataCenter::unLinstenDevRegisterNotify()
{
    const char *str = "datacenter/notify/spont/allApp/Updateregister";
    for (auto it=d_->handlers_.begin(); it!=d_->handlers_.end(); it++)
    {
        if (strcmp(it->first, str) == 0)
        {
            delete [] it->first;
            d_->handlers_.erase(it);
            unsubTopic(str);
            break;
        }
    }
}

void DataCenter::getRegister(const char *model_name)
{
    d_->registerGetRegister();

    mtx_.lock();
    if (model_name == nullptr)
        d_->model_devs_.clear();
    else
        d_->model_devs_.erase(model_name);
    mtx_.unlock();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());
    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();
    if (model_name)
        writer.String(model_name);
    writer.EndArray();
    writer.EndObject();

    sendMsg(d_->topics_->get_register, buffer.GetString());
}

bool DataCenter::getRegister(const char *model_name, size_t wait_s)
{
    getRegister(model_name);
    return d_->query_register_sem_.timedwait(wait_s*1000) == 0;
}

void DataCenter::unregisterDev(
                            std::string& model_name,
                            std::string& port,
                            std::string& addr,
                            std::string& desc)
{
    d_->registerUnregisterDev();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());

    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();
    writer.StartObject();
    writer.Key("model");
    writer.String(model_name.c_str());
    writer.Key("port");
    writer.String(port.c_str());
    writer.Key("addr");
    writer.String(addr.c_str());
    writer.Key("desc");
    writer.String(desc.c_str());
    writer.EndObject();
    writer.EndArray();

    writer.EndObject();

    sendMsg(d_->topics_->unregister_dev, buffer.GetString());
}

void DataCenter::unregisterDev(const char *model_name, const char *port,
                               const char *addr, const char *desc)
{
    d_->registerUnregisterDev();

    mtx_.lock();
    for (auto m=d_->model_devs_.begin(); m!=d_->model_devs_.end(); m++) {
        if (m->first.compare(model_name) == 0) {
            for (auto p=m->second.begin(); p!=m->second.end(); p++) {
                if (p->first.compare(port) == 0) {
                    for (auto d=p->second.begin(); d!=p->second.end(); ) {
                        if (d->addr.compare(addr) == 0) {
                            if (!desc || d->desc.compare(desc) == 0)
                                d = p->second.erase(d);
                            else
                                d++;
                        }
                        else
                            d++;
                    }
                }
            }
        }
    }
    mtx_.unlock();

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);

    writer.StartObject();
    writer.Key("token");
    writer.String(u32toa(++d_->token_).c_str());

    writer.Key("timestamp");
    writer.String(GetTimeStamp().c_str());

    writer.Key("body");
    writer.StartArray();
    writer.StartObject();
    writer.Key("model");
    writer.String(model_name);
    writer.Key("port");
    writer.String(port);
    writer.Key("addr");
    writer.String(addr);
    writer.Key("desc");
    if (desc)
        writer.String(desc);
    else
        writer.String("");
    writer.EndObject();
    writer.EndArray();

    writer.EndObject();

    sendMsg(d_->topics_->unregister_dev, buffer.GetString());
}

void DataCenter::onConnect()
{

}

void DataCenter::onConnectionLost(const char */*cause*/)
{

}

void DataCenter::onPublicFailure(const char */*cause*/, int /*code*/)
{

}

void DataCenter::onSubscribeFailure(const char */*cause*/, int /*code*/)
{

}

void DataCenter::onUnsubscribeFailure(const char */*cause*/, int /*code*/)
{

}

bool DataCenter::onMsgArrvd(const char */*topic*/, int /*topic_len*/, const char */*msg*/, int /*msg_len*/)
{
    return false;
}

void DataCenter::SubRemoteServiceInit()
{
    subTopic("RemoteService/set/request/SyyGather/initial_cfg");
}

void DataCenter::SubRemoteServiceHandle()
{
    subTopic("RemoteService/set/request/SyyGather/handle_cfg");
}